﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../global/head.h"
#include "../macro/head.h"
#include "aio_thread.h"
#include "aio_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos_aio
{
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static aio_impl::POOL * pool_ptr = 0;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    aio_impl::aio_impl()
    {
        m_lpGetAcceptExSockaddrs = 0;
        m_lpConnectEx = 0;
        m_lpAcceptEx = 0;

        m_pAioThread = 0;
        m_pCallback = 0;
        m_hIocp = 0;
    }

    aio_impl::~aio_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    aio_impl::lpGetAcceptExSockaddrs aio_impl::get_extrack_addr( SOCKET s )
    {
        int nRet = 0;

        DWORD dwBytes = 0, dwErr = 0;
        lpGetAcceptExSockaddrs f = 0;
        GUID guid = WSAID_GETACCEPTEXSOCKADDRS;

        dwErr = WSAIoctl( s, 
            SIO_GET_EXTENSION_FUNCTION_POINTER, 
            &guid, 
            sizeof( guid ), 
            &f,
            sizeof( f ), 
            &dwBytes, 
            NULL, 
            NULL ); 

        if( SOCKET_ERROR == dwErr ) 
        {
            dwErr = WSAGetLastError();
            nRet = 1;
            f = 0;
        }

        return f;
    }

    aio_impl::lpConnectEx aio_impl::get_connect_ex( SOCKET s )
    {
        int nRet = 0;

        DWORD dwBytes = 0, dwErr = 0;
        lpConnectEx f = 0;
        GUID guid = WSAID_CONNECTEX;

        dwErr = WSAIoctl( s, 
            SIO_GET_EXTENSION_FUNCTION_POINTER, 
            &guid, 
            sizeof( guid ), 
            &f,
            sizeof( f ), 
            &dwBytes, 
            NULL, 
            NULL ); 

        if( SOCKET_ERROR == dwErr ) 
        {
            dwErr = WSAGetLastError();
            nRet = 1;
            f = 0;
        }

        return f;
    }

    aio_impl::lpAcceptEx aio_impl::get_accept_ex( SOCKET s )
    {
        int nRet = 0;

        DWORD dwBytes = 0, dwErr = 0;
        lpAcceptEx f = 0;
        GUID guid = WSAID_ACCEPTEX;

        dwErr = WSAIoctl( s, 
            SIO_GET_EXTENSION_FUNCTION_POINTER, 
            &guid, 
            sizeof( guid ), 
            &f,
            sizeof( f ), 
            &dwBytes, 
            NULL, 
            NULL ); 

        if( SOCKET_ERROR == dwErr ) 
        {
            dwErr = WSAGetLastError();
            nRet = 1;
            f = 0;
        }

        return f;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int aio_impl::get_addr( POOL *** pppPool, LIST *** pppList )
    {
        int ret = 0;
        if( pppPool )
        {
            *pppPool = &pool_ptr;
        }
        if( pppList )
        {
            *pppList = 0;
        }
        return ret;
    }

    int aio_impl::notify( xos_box::i_msg *& p )
    {
        int nRet = 0;
        p->set_runnable( 0, m_pCallback );
        g_pSysData->m_pCallbackObj->notify( p );
        return nRet;
    }

    int aio_impl::init()
    {
        int nRet = 0;

        if( 0 == nRet )
        {
            SOCKET s = ::WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
            m_lpGetAcceptExSockaddrs = get_extrack_addr( s );
            m_lpConnectEx = get_connect_ex( s );
            m_lpAcceptEx = get_accept_ex( s );
            closesocket( s );
        }

        if( 0 == nRet )
        {
            m_hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0, 0, 1 );
            if( !m_hIocp )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            m_pAioThread = new aio_thread( this );
            m_pAioThread->init();
        }

        return nRet;
    }

    int aio_impl::term()
    {
        int nRet = 0;

        if( m_pAioThread )
        {
            PostQueuedCompletionStatus( m_hIocp, 0, 0, 0 );
            m_pAioThread->term();
            delete m_pAioThread;
            m_pAioThread = 0;
        }

        if( m_hIocp )
        {
            CloseHandle( m_hIocp );
            m_hIocp = 0;
        }

        m_lpGetAcceptExSockaddrs = 0;
        m_lpConnectEx = 0;
        m_lpAcceptEx = 0;

        m_pCallback = 0;
        m_hIocp = 0;

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // set callback. 
    // 
    int aio_impl::set_callback( xos::f_runnable pCallback )
    {
        int ret = 0;
        m_pCallback = pCallback;
        return ret;
    }

    // 
    // aio request
    // 
    // m_nData[0] : enumAioType
    // 
    int aio_impl::post_request( xos_box::i_msg *& pMsg )
    {
        int nRet = 0;

        aio_data * p = aio_data::get_item_from_pool( true );
        p->init();

        //m_nData[0];
        p->m_nType = ( i_aio::enumAioType )pMsg->get_int( 0, 0 );
        p->m_lpMsgData = pMsg;
        pMsg = 0;

        PostQueuedCompletionStatus( m_hIocp, 0, 0, ( WSAOVERLAPPED * )&p->m_ov_ex );
        p = 0;

        return nRet;
    }
} // xos_aio
